package genetic.q1;

import java.math.BigDecimal;
import java.util.Arrays;

import genetic.Genotype;

public class AckleyGenotype implements Genotype{
	//to represent the genotype, we will split the vector into 3 doubles and use binary numbers to represent each of them,
	//the binary number will consist  bits - 32 to -32,
	//the second binary number will be 10 bits - 0 to 1024 (genotypes with more then 999 will be discarded)

	double[] val;
	boolean[] bitString;

	public AckleyGenotype(double[] val) {
		this.val = val;
		this.bitString = makeBitString();
	}

	public AckleyGenotype(boolean[] bitString) {
		this.bitString = bitString;
		this.val = makeValue();

	}

	public AckleyGenotype clone(){
		return new AckleyGenotype(Arrays.copyOf(val, val.length));
	}

	private double[] makeValue(){
		double[] ans = new double[3];
		ans[0] = round(binToDouble(bitString, 0), 3);
		ans[1] = round(binToDouble(bitString, 16), 3);
		ans[2] = round(binToDouble(bitString, 32), 3);
		return ans;
	}

	private boolean[] makeBitString(){
		boolean[] ans = new boolean[48];
		doubleToBin(val[0], ans, 0);
		doubleToBin(val[1], ans, 16);
		doubleToBin(val[2], ans, 32);
		return ans;
	}

	private double binToDouble(boolean[] bin, int start){
		double ans = 0;
		double tmp = 16768;
		for(int i=1; i<16; i++){
			ans = ans + (bin[i+start] ? tmp : 0);
			tmp = tmp/2;
		}
		ans = ans * (bin[start]==false ? 1 : -1);
		return ans/1000.0;

	}

	private boolean[] doubleToBin(double val, boolean[] ans, int start){
		if (val<0){
			ans[start] = true;
		} else {
			ans[start] = false;
		}
		int index = start+1;
		int num1 = (int)(val*1000);
		int tmp = 16384;
		for (;tmp>=1; tmp/=2){
			ans[index] = (num1/tmp == 1);
			index++;
			num1 = num1 % tmp;
		}
		return ans;
	}

	@Override
	public boolean[] getBitString() {
		return bitString;
	}

	public double[] getVal() {
		return val;
	}

	@Override
	public String toString() {
		return Arrays.toString(val);
	}

	public static double round(double d, int decimalPlace){
		BigDecimal bd = new BigDecimal(Double.toString(d));
		bd = bd.setScale(decimalPlace,BigDecimal.ROUND_HALF_UP);
		return bd.doubleValue();
	}
}
